package amtf

import (
	"context"
	"fmt"
	"reflect"
	"strings"

	"server/global"
	"server/model/system"

	"github.com/gin-gonic/gin"
	"github.com/pkg/errors"
	"go.uber.org/zap"
	"gorm.io/datatypes"
)

type S插件 struct {
	Name  string             `json:"name"`
	A英文名  string             `json:"英文名" gorm:"column:英文名;comment:;"`
	A组件s  []*S组件             `json:"组件s" gorm:"-"`
	Icon  string             `json:"icon"`
	Amenu system.SysBaseMenu `json:"menu" gorm:"-"`
}

// func (S插件) TableName() string {
// 	return "a插件"
// }

type S组件 struct {
	Name       string `json:"name"`
	A英文名       string `json:"英文名"`
	A插件名       string `json:"插件名"`
	Icon       string `json:"icon"`
	Aapi       *Sapi  `gorm:"-"`
	A其他api     any
	Aservice   *Sservice
	Amodel     any `json:"model"`
	A列表req     S列表req
	A字段组       []*S字段 `json:"字段组"`
	A组件关联包s    []string
	Amodel关联包s []string
	A权限集       *S权限集   `json:"权限集"`
	A手动挡       *S组件手动挡 `gorm:"-"`
	A手动挡有否     bool    `json:"手动挡有否"`
}

type S字段 struct {
	Name         string `json:"name"`
	Name_go      string
	A类型          string `json:"类型"`
	A类型_go       string
	Icon         string            `json:"icon"`
	A是主键         bool              `json:"是主键"`
	DefaultValue string            `json:"defaultValue"`
	Comment      string            `json:"comment"`
	DataTypeLong string            `json:"dataTypeLong"`
	A字段值选项       datatypes.JSON    `json:"字段值选项"`
	A关联属性        map[string]string `json:"关联属性"`
	A关联组件        *S组件              `gorm:"-"`
	A省略gorm      bool
	A不入库         bool `json:"不入库"`
	A关联包名        string
	A外键字段名       string
	A指定外键标签      string
	S插件ID        uint
	S组件ID        uint
}

type S组件手动挡 struct {
	A注册路由 any `gorm:"-"`
	// A注册路由    func(*gin.RouterGroup, *gin.RouterGroup) `gorm:"-"`
	A增s7     any
	A改之前s7   any
	Aservice any
}

var A插件集 map[string]*S插件
var A组件集 map[string]*S组件

func init() {
	A插件集 = make(map[string]*S插件)
	A组件集 = make(map[string]*S组件)
}

// 用反射动态生成的model，无法给gorm使用
func A予插件_基础部分(插件 *S插件) {
	A插件集[插件.Name] = 插件
	// 先循环赋值
	for _, 组件 := range 插件.A组件s {
		组件.A插件名 = 插件.Name
		组件.A手动挡 = &S组件手动挡{}
		A组件集[插件.Name+"."+组件.Name] = 组件
	}
	// 后循环取用，判断model对应字段类型
	for _, 组件 := range 插件.A组件s {
		var 本组件拟添加外键字段s []*S字段
		for _, 字段 := range 组件.A字段组 {
			if 字段.Name_go == "" {
				字段.Name_go = "A" + 字段.Name
			}
			if 字段.A类型_go == "" {
				字段.A类型_go = 字段.A类型
			}
			switch 字段.A类型 {
			case "关联记录":
				关联id := 字段.A关联属性["关联id"]
				关联类型 := 字段.A关联属性["关联类型"]
				// gorm默认对应外键
				外键字段 := &S字段{}
				字段.A省略gorm = true
				// 字段.A不入库 = true

				关联组件 := A组件集[关联id]
				字段.A关联组件 = 关联组件
				var 关联包名 string
				if 关联组件 != nil {
					关联插件名 := 关联组件.A插件名
					关联插件 := A插件集[关联插件名]

					//形如： Askus  []shangpinsku.Sskumodel  `json:"skus" gorm:"column:skus;"`
					// 字段.A关联属性 = 关联属性
					关联包名 = fmt.Sprintf("server/aa/%s/%s", 关联插件.A英文名, 关联组件.A英文名)
					字段.A类型_go = fmt.Sprintf("%s.S%smodel", 关联组件.A英文名, 关联组件.Name)

					外键字段.Name = 组件.Name + "id"
					外键字段.Name_go = "S" + 组件.Name + "modelID"
				} else {
					// 是内置组件
					sps := A拆分字符(关联id)
					关联包名 = fmt.Sprintf("server/model/%s", sps[0])
					字段.A类型_go = 关联id
					外键字段.Name = sps[1] + "ID"
					外键字段.Name_go = 外键字段.Name
				}

				组件.Amodel关联包s = append(组件.Amodel关联包s, 关联包名)

				外键字段.A类型_go = "uint"
				字段.A外键字段名 = 外键字段.Name_go

				// 外键标签 := fmt.Sprintf("->;foreignKey:%s;", 外键字段.Name_go)
				if strings.Contains(关联类型, "属于") {
					// 属于关系：把关联组件的id存到自己这里
					if 关联组件 != nil {
						外键字段.Name = 关联组件.Name + "id"
						外键字段.Name_go = "S" + 关联组件.Name + "modelID"
					}
					// if 关联类型 == "一对一属于" {
					// 	// 组件.A字段组 = append(组件.A字段组, 外键字段)
					// }
					本组件拟添加外键字段s = append(本组件拟添加外键字段s, 外键字段)

					字段.A省略gorm = false
					字段.A指定外键标签 = fmt.Sprintf("->;foreignKey:%s;", 外键字段.Name_go)
				} else {
					// 包含关系：把自己的id存到关联组件中去
					if 关联类型 == "一对多" {
						字段.A省略gorm = true
						字段.A类型_go = fmt.Sprintf("[]%s", 字段.A类型_go)
					} else if 关联类型 == "一对一包含" {
						// pass
						// 字段.A指定外键标签 = 外键标签
					}
					关联组件.A字段组 = append(关联组件.A字段组, 外键字段)
				}

			// case "外键":
			// 	字段.Name_go = "S" + strings.TrimSuffix(字段.Name, "id") + "modelID"
			// 	字段.A类型_go = "uint"
			case "富文本", "json", "arr", "arr包对象", "图片组", "文件":
				字段.A类型_go = "datatypes.JSON"
				// 组件.A有json = true
				// 组件.A组件关联包s = append(组件.A组件关联包s, "gorm.io/datatypes")
				组件.Amodel关联包s = append(组件.Amodel关联包s, "gorm.io/datatypes")
			case "图片", "视频":
				字段.A类型_go = "string"
			case "enum":
				字段.A类型_go = "string"
			case "time":
				字段.A类型_go = "*time.Time"
				组件.Amodel关联包s = append(组件.Amodel关联包s, "time")
				// case "不入库":
				// 	字段.A类型_go = "datatypes.JSON"
				// 	字段.A不入库 = true
				// 	组件.A有json = true
			}
		}
		组件.Amodel关联包s = A字符串切片去重(组件.Amodel关联包s)
		组件.A字段组 = A字段组去重(组件.A字段组)
		if len(本组件拟添加外键字段s) > 0 {
			组件.A字段组 = append(组件.A字段组, 本组件拟添加外键字段s...)
		}
	}
}

func A注册插件(PrivateGroup *gin.RouterGroup, PublicGroup *gin.RouterGroup, engine *gin.Engine, 插件 *S插件) {
	// A插件集[插件.Name] = 插件
	插件名 := 插件.Name
	插件英文名 := 插件.A英文名
	插件icon := 插件.Icon

	PrivateGroup = PrivateGroup.Group(插件英文名)
	PublicGroup = PublicGroup.Group(插件英文名)

	menu := A注册目录_单个(system.SysBaseMenu{
		MenuLevel: 0, Hidden: false, Path: 插件英文名, Name: 插件名, Component: "layouts/父路由.vue", Sort: 0, Meta: system.Meta{Title: 插件名, Icon: 插件icon}})

	插件.Amenu = menu
	for _, 组件 := range 插件.A组件s {
		A注册组件(PrivateGroup, PublicGroup, engine, 插件, 组件)
		// A组件集[插件.Name+"."+组件.Name] = 组件
	}
}

func A注册组件(PrivateGroup *gin.RouterGroup, PublicGroup *gin.RouterGroup, engine *gin.Engine, 插件 *S插件, 组件 *S组件) {
	ctx := context.Background()
	err := global.GVA_DB.WithContext(ctx).AutoMigrate(组件.Amodel)
	if err != nil {
		err = errors.Wrap(err, "注册表失败!")
		zap.L().Error(fmt.Sprintf("%+v", err))
	}
	插件名 := 插件.Name
	插件英文名 := 插件.A英文名

	组件名 := 组件.Name
	组件英文名 := 组件.A英文名
	组件icon := 组件.Icon

	// 双向关联👇
	api := Sapi{}
	api.A组件 = 组件
	组件.Aapi = &api

	service := Sservice{}
	service.A组件 = 组件
	组件.Aservice = &service

	// PrivateGroup = PrivateGroup.Group(组件英文名)
	PrivateGroup = PrivateGroup.Group(组件英文名)
	PublicGroup = PublicGroup.Group(组件英文名)
	if 组件.A权限集 != nil && 组件.A权限集.A全公开 {
		PublicGroup.POST("", api.A增)
		PublicGroup.DELETE("", api.A删)
		PublicGroup.PUT("", api.A改)
	} else {
		if 组件.A权限集 != nil && 组件.A权限集.A增 != nil && 组件.A权限集.A增.A公开 {
			PublicGroup.POST("", api.A增)
		} else {
			PrivateGroup.POST("", api.A增)
		}
		PrivateGroup.DELETE("", api.A删)
		PrivateGroup.PUT("", api.A改)
	}
	// 根据权限处理api(PrivateGroup, PublicGroup, 组件, api, "增")
	// PrivateGroup.DELETE("", api.A删)
	// PrivateGroup.PUT("", api.A改)
	// PrivateGroup.GET("", api.A查)

	PublicGroup.GET("查字段值选项集", api.A查字段值选项集)
	PublicGroup.GET("", api.A查)

	if 组件.A手动挡.A注册路由 != nil {
		// 组件.A手动挡.A注册路由(PrivateGroup, PublicGroup)
		A反射调用(组件.A手动挡.A注册路由, PrivateGroup, PublicGroup)
	}
	// 其他api := 组件.A其他api
	// if 其他api != nil {
	// 	value := reflect.ValueOf(其他api)
	// 	typ := reflect.TypeOf(其他api)
	// 	for i := 0; i < typ.NumMethod(); i++ {
	// 		method := typ.Method(i)
	// 		// 注册方法到 group
	// 		group.GET(method.Name, func(c *gin.Context) {
	// 			value.MethodByName(method.Name).Call([]reflect.Value{reflect.ValueOf(c)})
	// 		})
	// 	}
	// }

	entities := []system.SysApi{
		{ApiGroup: fmt.Sprintf("%s/%s", 插件名, 组件英文名), Method: "POST", Path: fmt.Sprintf("/%s/%s", 插件英文名, 组件英文名), Description: fmt.Sprintf("增%s", 组件名)},
		{ApiGroup: fmt.Sprintf("%s/%s", 插件名, 组件英文名), Method: "GET", Path: fmt.Sprintf("/%s/%s", 插件英文名, 组件英文名), Description: fmt.Sprintf("查%s", 组件名)},
		{ApiGroup: fmt.Sprintf("%s/%s", 插件名, 组件英文名), Method: "PUT", Path: fmt.Sprintf("/%s/%s", 插件英文名, 组件英文名), Description: fmt.Sprintf("改%s", 组件名)},
		{ApiGroup: fmt.Sprintf("%s/%s", 插件名, 组件英文名), Method: "DELETE", Path: fmt.Sprintf("/%s/%s", 插件英文名, 组件英文名), Description: fmt.Sprintf("删%s", 组件名)},
	}
	A注册api(entities...)
	前端路由path := 组件英文名
	前端路由名 := "list"
	if 组件名 == "设置" {
		前端路由名 = 组件名
	}
	前端路由名 = fmt.Sprintf("%s/%s/%s", 插件名, 组件名, 前端路由名)

	menus := []system.SysBaseMenu{
		{MenuLevel: 0, Hidden: false, Path: 前端路由path, Name: 前端路由名, Component: "找aa", Sort: 1, Meta: system.Meta{Title: 组件名, Icon: 组件icon}, ParentId: 插件.Amenu.ID},
	}
	A注册目录_多个(menus)
}

func 包装api(apiFunc func(*gin.Context, S组件), 组件 S组件) gin.HandlerFunc {
	return func(c *gin.Context) {
		apiFunc(c, 组件)
	}
}

func A取Api(value reflect.Value, methodName string) gin.HandlerFunc {
	// 获取 Aapi 字段
	aapiField := value.FieldByName("Aapi")
	if !aapiField.IsValid() {
		panic(fmt.Sprintf("组件中未找到 Aapi 字段"))
	}
	// 如果 Aapi 字段不是指针类型，将其转换为指针类型
	if aapiField.Kind() != reflect.Ptr {
		aapiField = aapiField.Addr()
	}
	// 在 Aapi 字段对应的结构体中查找方法
	method := aapiField.MethodByName(methodName)
	if !method.IsValid() {
		panic(fmt.Sprintf("未找到 %s 方法", methodName))
	}
	return func(c *gin.Context) {
		method.Call([]reflect.Value{reflect.ValueOf(c)})
	}
}

func getApiFunction(value reflect.Value, methodName string) gin.HandlerFunc {
	// 获取 Aapi 字段
	aapiField := value.FieldByName("Aapi")
	if !aapiField.IsValid() {
		panic(fmt.Sprintf("组件中未找到 Aapi 字段"))
	}
	// 如果 Aapi 字段不是指针类型，将其转换为指针类型
	if aapiField.Kind() != reflect.Ptr {
		aapiField = aapiField.Addr()
	}
	// 在 Aapi 字段对应的结构体中查找方法
	method := aapiField.MethodByName(methodName)
	if !method.IsValid() {
		panic(fmt.Sprintf("未找到 %s 方法", methodName))
	}
	return func(c *gin.Context) {
		method.Call([]reflect.Value{reflect.ValueOf(c)})
	}
}

func A解构组件(组件 *S组件) (model any, models any, service *Sservice) {
	model = 组件.Amodel
	model = A实例转指针(model)
	models = A成切片指针(model)
	service = 组件.Aservice
	return
}
